package event.timerevent;

import java.io.Serializable;

/**rappresenta la classe su cui e' possibile applicare listener di tipo Time.
 * <p>
 *  <p>PERCHE' NEL MAIN DEVO SPECIFICARE ANCHE QUESTA CLASSE?</p>
 * forse ti potresti chiedere perche', oltre all'implementazione del TimerListener, devi implementare anche
 * questa classe (cioe' quello che fai ogni volta che implementi Runnable o Serializaible). La risposta la
 * puoi capire se facciamo il paragone con ActionListener (l'interfaccia che opera sui pulsanti). Per poter
 * funzionare tale interfaccia ha infatti bisogno, oltre che dell'implementazione di ActionPerformed, anche
 * di almeno un'istanza di Button (cioe' il pulsante su cui ActionPerformed si basa). Lo stesso avviene con
 * TimeListener: il Time, per poter funzionare, non solo ha bisogno dell'implementazione di TimerRung ma anche
 * di almeno un'instanza di un particolare oggetto basilare; per ActionListener tale oggetto e' Button, mentre
 * per TimerListener l'oggetto in questione e' Timer. Spero di essere stato chiaro!
 * </p>
 * <p> 
 *  <p>STATUS DEL TIMER</p>
 *  il timer puo' essere impostato su 3 differenti status, cioe' proprieta' del timer stesso:
 *   <p>ACTIVE in questa modalita' il Timer scorre normalmente contando unita' di tempo come fa un normale cronometro.
 *  e come un normale cronometro esso puo' essere fermato (o addirittura azzerato) mentre sta ancora conteggiando il
 *  tempo. nella modalita' ACTIVE quindi possono avvenire 2 situazioni diverse:
 *    <p>il cronometro viene fermato: durante il conteggio del tempo qualcosa richiama il metodo StopTimer o SuspendTimer;
 *    entrambi sortiscono l'effetto di fermare il cronometro che dunque non raggiugnera' mai il tmepo da te prestabilito
 *    ma si generera' l'evento TimerPaused o TimerSuspended
 *    <p>il cronometro raggiunge il tempo da te stablilito: in questo caso viene generato l'evento TimerRung. quindi lo
 *    status del Timer viene azzerato a SUSPENDED 
 *   </p>
 *   <p>PAUSED in questa modalita' il timer smette di conteggiare il tempo ma il numero di unita' temporali che ha conteggiato
 *   viene memorizzato. in questo modo e' possibile in un secondo tempo riavviare il timer (tramite startTimer)
 *   </p>
 *   <p>SUSPENDED in questa modalita' il timer smette di conteggiare il tempo e NON viene salvato il numero di unita'
 *   temporali conteggiate. in questo modo il Timer viene azzerato.
 *   </p>
 * </p>
 * 
 * @author koldar
 *
 */
public class Timer implements Serializable,Runnable{

	private static final long serialVersionUID = -9169475848000337328L;
	/**indica che il cronometro sta scorrendo*/
	public static final int ACTIVE=0;
	/**indica che il cronometro e' fermo ed attende che venga riattivato*/
	public static final int PAUSE=1;
	/**indica che il cronometro e' stato sospeso*/
	public static final int SUSPENDED=2;
	
	/**indica il numero di TimeGap (ovvero di unit� di tempo) che il Timer aspetta prima di squillare. Per esempio, se
	 * vuoi aspettare 10 secondi prima di far squillare il Timer imposta TimeGap su "1000" e NumberLaps su "10".
	 * <p>NOTA non si puo' modificare questo parametro se il Timer e' ACTIVE*/ 
	private int NumberLaps;
	/**indica l'unit� di tempo che il timer aspetta per ogni ciclo di timer. per esempio, se vuoi che il timer conti a secondi,
	 * imposta il parametro su 1000 (1000 millisecondi sono 1 secondo). se vuoi che conti a minuti ipostalo invece su 60000.
	 * <p>NOTA non si puo' modificare questo parametro se il Timer e' ACTIVE*/
	private long TimeGap;
	/**indica il Thread che conta il tempo passato per il cronometro*/
	private Thread ThisThread;
	/**indica la classe che incamerata tutte le classi che sono interessate a questo evento, oltre all'oggetto che spedir� eventi e, in gneerale, li gestira'*/
	private TimerManager EventManager;
	/**indica lo stato del Timer (Attivo,Pausa,Sospeso)*/
	private int StatusTimer;
	/**se il Timer e' stato messo in pausa, indica quanti unita' di tempo sono state gia' eseguite*/
	private int NumberLapsDone;
	/**se TRUE continua a ripetere il Timer*/
	private boolean Repeat;
	
	/**crea un timer che aspetta 5 secondi prima di squillare (senza ripetizione)*/
	public Timer(){
		this.TimeGap=1000;
		this.NumberLaps=5;
		this.EventManager=new TimerManager(this);
		this.setStatusTimer(SUSPENDED);
		this.NumberLapsDone=0;
		this.Repeat=false;
	}
	/**crea un timer che aspetta un numero di volte pari a numberlaps il tempo specificato dal parametro timelap.
	 * <p>ESEMPIO un cronometro che conta 10 secondi avr� come parametri timegap=1000 e numberlaps=10
	 * <p>NOTE il costruttore fa una verifica che i parametri siano positivi non nulli. se non lo sono, essi veranno
	 * impostati come default (timegap=1000 numberlaps=5)
	 * <p>NOTE il timer non viene ripetuto
	 * @param timegap indica il tempo che il timer aspetta ad ogni lap del cronometro
	 * @param numberlaps indica il numero di lap che il cronometro deve fare prima di squillare
	 */
	public Timer(long timegap,int numberlaps){
		if (timegap<=0){
			timegap=1000;
		}
		if (numberlaps<=0){
			numberlaps=5;
		}
		this.TimeGap=timegap;
		this.NumberLaps=numberlaps;
		this.EventManager=new TimerManager(this);
		this.setStatusTimer(SUSPENDED);
		this.NumberLapsDone=0;
		this.Repeat=false;
	}
	/**
	 * @return the repeat
	 */
	public boolean isRepeat() {
		return Repeat;
	}
	/**
	 * @param repeat the repeat to set
	 */
	public void setRepeat(boolean repeat) {
		if (this.isActive()!=true){
			Repeat = repeat;
		}
	}
	/**
	 * @return the numberLaps
	 */
	public int getNumberLaps() {
		return NumberLaps;
	}
	/**imposta un nuovo valore di NumberLaps. questa funzione non esegue nulla se il Cronometro ha status ACTIVE
	 * @param numberLaps the numberLaps to set
	 */
	public void setNumberLaps(int numberLaps) {
		if (this.StatusTimer!=Timer.ACTIVE){
			NumberLaps = numberLaps;
		}
	}
	/**
	 * @return the timeGap
	 */
	public long getTimeGap() {
		return TimeGap;
	}
	/**imposta un nuovo valore di TimeGap. questa funzione non esegue nulla se il Cronometro ha status ACTIVE
	 * @param timeGap the timeGap to set
	 */
	public void setTimeGap(long timeGap) {
		if (this.StatusTimer!=Timer.ACTIVE){
			TimeGap = timeGap;
		}
	}
	/**
	 * @return the statusTimer
	 */
	public int getStatusTimer() {
		return StatusTimer;
	}
	/**
	 * 
	 * @param ST imposta il valore di StatuTimer da usare
	 */
	private void setStatusTimer(int ST){
		this.StatusTimer=ST;
	}
	/**
	 * @return TRUE se il Timer sta funzoinando
	 */
	public boolean isActive(){
		return (StatusTimer==Timer.ACTIVE)?true:false;
	}
	/**
	 * 
	 * @return TRUE se il Timer e' stato messo in pausa
	 */
	public boolean isPaused(){
		return (StatusTimer==Timer.PAUSE)?true:false;
	}
	/**
	 * 
	 * @return TRUE se il Timer e' stato sospeso
	 */
	public boolean isSuspended(){
		return (StatusTimer==Timer.SUSPENDED)?true:false;
	}
	/**
	 * 
	 * @return TRUE se il timer al momento e' fermo (e' o in PAUSE o in SUSPENDED)
	 */
	public boolean isFreezed(){
		return !isActive();
	}
	/**usa la classe per dire che ti interessano eventi TimeListener*/
	public void addTimerListener(TimerListener TL){
		this.EventManager.addTimerListener(TL);
	}
	/**rimuove il Listener del TimeListener selezionato*/
	public void removeTimerListener(TimerListener TL){
		this.EventManager.removeTimerListener(TL);
	}
	/**fa partire il cronometro. Questa metodo non esegue nulla se il Timer ha gia' come status Timer.ACTIVE*/
	public void StartTimer(){
		if (this.StatusTimer!=Timer.ACTIVE){
			this.ThisThread=new Thread(this);
			ThisThread.start();
			this.setStatusTimer(ACTIVE);
		}
	}
	/**mette in pausa il Timer (lo Status va a Timer.PAUSE). Questa azione puo' essere eseguita se e solo se
	 * il Timer ha status Timer.ACTIVE*/
	public void StopTimer(){
		if (this.StatusTimer==Timer.ACTIVE){
			this.setStatusTimer(PAUSE);
			ThisThread=null;
		}
	}
	/**sospende il Timer. Questo comporta che il Timer viene messo in pausa e il cronometro viene
	 * azzerato; cioe' portato a numero di giri completati pari a 0*/
	public void SuspendTimer(){
		this.setStatusTimer(SUSPENDED);
		ThisThread=null;
		this.NumberLapsDone=0;
	}
	@Override
	public void run() {
		int i=this.NumberLapsDone;
		do{
			while ((i<NumberLaps)&&(this.StatusTimer==Timer.ACTIVE)){
				try {
					Thread.sleep(this.TimeGap);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}	
				i++;
			}
			this.NumberLapsDone=i;
			int tempStatus=this.StatusTimer;
			if (this.getStatusTimer()==Timer.ACTIVE){
				SuspendTimer();
			}
			this.EventManager.sendTimerEvent(tempStatus,i);
		}while (this.Repeat);
	}

}
